package poc;

import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.toc.TocHelper;
import org.docx4j.wml.*;
import poc.utils.ParagraphUtil;

import javax.xml.bind.JAXBElement;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by LiuLin on 2018/10/24.
 */
public class DocxToTxt {

	private  static ObjectFactory wmlObjectFactory = new ObjectFactory();
	public static final String FILE_SEPARATOR = isWindowsPlatform() ? "\\\\" : System.getProperty("file.separator");

	public static void main(String[] args) throws Exception {

		String docPath = "D:\\MetaSota\\翻译对齐结果\\11.13对齐文件及结果\\IBRD_UA_EN.DOCX";
		String txtPath = "D:\\MetaSota\\翻译对齐结果\\11.13对齐文件及结果\\IBRD_UA_EN.txt";
		//docxToTxt(docPath,txtPath);

		parserDocxAndWrite(docPath, txtPath, false);

//		String bilingualDocxPath = "D:\\MetaSota\\翻译对齐结果\\双语docx\\1-1.备忘录 华瑞制药吸收合并北京费森所涉主要步骤及相关问题_原件(CHN).docx";
//		bilingualDocxToTxt(bilingualDocxPath);

//		String str = "dasdsa____dsdad_____dsadas_dsadasd_dadds_";
//		String str02 = dealWithDocxParagraph(str);
//		System.out.println("str02 === " + str02);

//		String test = "测试";
//		String unicode = stringToUnicode(test);
//		System.out.println(unicode);
//		String string = unicodeToString(unicode);
//		System.out.println(string);
//
//		String stringxx = unicodeToString("\\u0002");
//		System.out.println(stringxx);
//		char[] chars = stringxx.toCharArray();
//		for(char c: chars){
//			System.out.println("c ====" + c);
//			System.out.println("c unicode ====" + stringToUnicode(c+""));
//		}

	}

	//字符串转换unicode
	public static String stringToUnicode(String string) {
		StringBuffer unicode = new StringBuffer();
		for (int i = 0; i < string.length(); i++) {
			char c = string.charAt(i);  // 取出每一个字符
			unicode.append("\\u" +Integer.toHexString(c));// 转换为unicode
		}
		return unicode.toString();
	}

	//unicode 转字符串
	public static String unicodeToString(String unicode) {
		StringBuffer string = new StringBuffer();
		String[] hex = unicode.split("\\\\u");
		for (int i = 1; i < hex.length; i++) {
			int data = Integer.parseInt(hex[i], 16);// 转换出每一个代码点
			string.append((char) data);// 追加成string
		}
		return string.toString();
	}

	/**
	 * 将docx文档转txt
	 * @param docxPath:文档路径
	 * @param txtPath:生成txt的路径
	 */
	public static void docxToTxt(String docxPath,String txtPath) throws Exception{
		WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(docxPath));
		List<Object> paragraphs = TocHelper.getAllElementsFromObject(wordMLPackage.getMainDocumentPart(), P.class);

		String txtContent = "";

		for(Object p : paragraphs){
			String pText= TextUtils.getText(p);
			pText = dealWithDocxParagraph(pText);
			if(isSignaturePageSign(pText)){
				//签字页不要
				break;
			}
			if(pText.length() == 0){
				continue;
			}
			//System.out.println("paragraph text:" + pText);
			txtContent += pText + "\n";
		}
		writeTxtFile(txtPath,txtContent);
	}

	/**
	 * 将双语docx文档转换为一对中英文的txt
	 * @param docxPath:文档路径
	 */
	public static void bilingualDocxToTxt(String docxPath) throws Exception{

		WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(docxPath));
		List<Object> paragraphs = TocHelper.getAllElementsFromObject(wordMLPackage.getMainDocumentPart(), P.class);

		List<String> allParagraph = new ArrayList<>();
		//循环所有的段落，对每个段落进行字符处理后加入到list中
		for(Object p : paragraphs){
			String pText = TextUtils.getText(p);
			//System.out.println("paragraph text:" + pText);
			if(!pText.trim().equals("")){
				pText = dealWithDocxParagraph(pText);
				//System.out.println("paragraph text:" + pText);
				allParagraph.add(pText);
			}
		}

		//中文txt内容
		String zhTxtContent = "";
		//英文txt内容
		String enTxtContent = "";

		//中文段落集合
		List<String> zhParagraphList = new ArrayList<>();
		//英文段落集合
		List<String> enParagraphList = new ArrayList<>();

		//前一个句子的语言
		String preParagraphLan = "ot";
		//当前句子的语言
		String curParagraphLan = "ot";

		//循环处理段落list，并将其分为中英文两个list
		for(int i=0;i<allParagraph.size();i++){
			String paragraph = allParagraph.get(i).trim();
			if(paragraph.length() > 0){
				curParagraphLan = getSentenceLan(paragraph);
				if(curParagraphLan.equals("ot") && i < (allParagraph.size() - 1)){
					continue;
				}
			}else{
				continue;
			}

			if(i == allParagraph.size() - 1){
				if(curParagraphLan.equals("zh")){
					zhParagraphList.add(paragraph);
				}else if(curParagraphLan.equals("en")){
					enParagraphList.add(paragraph);
				}
			}

			//当当前段落的语言种类与上一段不同时，需要处理中英文段落集合
			if(zhParagraphList.size() > 0 && enParagraphList.size() > 0 && (!curParagraphLan.equals(preParagraphLan) || isSignaturePageSign(paragraph) || i == (allParagraph.size() - 1))){

				if(zhParagraphList.size() > enParagraphList.size()){
					//如果中文段落比英文段落多，则中文只取靠近英文的相同个数的段落,英文段落全取
					enTxtContent += getListContentByLength(enParagraphList,enParagraphList.size(),false,false);
					zhTxtContent += getListContentByLength(zhParagraphList,enParagraphList.size(),false,true);
				}else if(enParagraphList.size() > zhParagraphList.size()){
					//如果英文段落比中文段落多，则英文只取靠近中文的相同个数的段落，中文全取
					zhTxtContent += getListContentByLength(zhParagraphList,zhParagraphList.size(),false,false);
					enTxtContent += getListContentByLength(enParagraphList,zhParagraphList.size(),false,true);
				}else{
					//相同则都全取
					zhTxtContent += getListContentByLength(zhParagraphList,zhParagraphList.size(),false,false);
					enTxtContent += getListContentByLength(enParagraphList,enParagraphList.size(),false,false);
				}

				zhParagraphList.clear();
				enParagraphList.clear();

			}

			if(isSignaturePageSign(paragraph)){
				//签字页丢弃
				break;
			}

			if(curParagraphLan.equals("zh")){
				zhParagraphList.add(paragraph);
				preParagraphLan = "zh";
			}else if(curParagraphLan.equals("en")){
				enParagraphList.add(paragraph);
				preParagraphLan = "en";
			}

		}

		String docxZhPath = docxPath.replace(".docx","") + "_zh.txt";
		String docxEnPath = docxPath.replace(".docx","") + "_en.txt";

		writeTxtFile(docxZhPath,zhTxtContent);
		writeTxtFile(docxEnPath,enTxtContent);

	}

	/**
	 * 处理文档的段落信息
	 * @param paragraph:文档段落
	 */
	public static String dealWithDocxParagraph(String paragraph){
		if(paragraph.contains("PAGEREF _Toc")){
			//去掉目录信息
			paragraph = paragraph.substring(0,paragraph.indexOf("PAGEREF _Toc"));
		}
		paragraph = paragraph.replace("\t"," ");
		paragraph = paragraph.replace("\\h"," ");
		paragraph = paragraph.replace("\\u"," ");
		paragraph = paragraph.replace("\\z"," ");
		paragraph = paragraph.replace("\\o"," ");
		paragraph = paragraph.replace("[*]",unicodeToString("\\u0002"));
		paragraph = paragraph.replace("【】",unicodeToString("\\u0002"));
		paragraph = paragraph.replace("【 】",unicodeToString("\\u0002"));
		paragraph = paragraph.replace("【  】",unicodeToString("\\u0002"));
		paragraph = paragraph.replace("【  】",unicodeToString("\\u0002"));
		paragraph = paragraph.replace("【    】",unicodeToString("\\u0002"));
		paragraph = dealWithSentenceSpecialCharacters(paragraph);
		paragraph = paragraph.trim();
		if(paragraph.equals("(") || paragraph.equals(")") || paragraph.equals("#") || paragraph.equals("……") || paragraph.equals("......")){
			paragraph = "";
		}
		return paragraph;
	}

	/**
	 *
	 * 从集合里面取指定个数的字符串并根据传入的needMerge判断是否需要用换行符隔开
	 * @param stringList:字符串集合
	 * @param length:需取的字符串个数
	 * @param needMerge:表示是否需要用换行符隔开,true表示需要
	 * @param reverseOrder:表示是否需要倒着取值，true表示需要
	 */
	public static String getListContentByLength(List<String> stringList,int length,boolean needMerge,boolean reverseOrder){
		String result = "";
		if(!reverseOrder){
			for(int i=0;i<stringList.size();i++){
				if(i < length){
					result += stringList.get(i) + "\n";
				}else{
					break;
				}
			}
		}else{
			for(int i = stringList.size() - 1;i >= 0;i--){
				if(i >= (stringList.size() - length)){
					result += stringList.get(i) + "\n";
				}else{
					break;
				}
			}
		}
		if(needMerge){
			result = result.replace("\n"," ");
			result = result + "\n";
		}
		return result;
	}

	/**
	 * 处理字符串的连续下划线，并将其替换成#
	 * 将字符串的连续空格替换成两个空格
	 * @param str:需处理的字符串
	 */
	public static String dealWithSentenceSpecialCharacters(String str){
		if(!str.contains("__") && !str.contains("  ") && !str.contains(".......") && !str.contains("……")){
			return str;
		}else{
			char[] chars = str.toCharArray();
			String newStr = "";
			for(int i=0;i<chars.length;i++){
				if((chars[i]+"").equals("_")){
					if(!newStr.endsWith("__")){
						newStr += chars[i];
					}
				} else if((chars[i]+"").equals(" ")){
					if(!newStr.endsWith("  ")){
						newStr += chars[i];
					}
				}else if((chars[i]+"").equals(".")){
					if(!newStr.endsWith("......")){
						newStr += chars[i];
					}
				}else if((chars[i]+"").equals("…")){
					if(!newStr.endsWith("……")){
						newStr += chars[i];
					}
				}else{
					newStr += chars[i];
				}
			}
			newStr = newStr.replace("__",unicodeToString("\\u0001"));
			return newStr;
		}
	}

	/**
	 * 判断句子的语言："zh"：中文，"en"：英文，"ot"：其它
	 * @param sentence:需判断的句子
	 */
	public static String getSentenceLan(String sentence){
		if(sentence == null || sentence.length() == 0){
			return "ot";
		}
		if(sentence.contains("《") && sentence.contains("》")){
			//特殊的中文符号
			return "zh";
		}
		int zhNum = 0;
		int enNum = 0;
		char[] chars = sentence.toCharArray();
		for(char c:chars){
			if('\u4e00' <= c && c <= '\u9fa5'){
				zhNum++;
			}else if((c >= '\u0041' && c <= '\u005a') || (c >= '\u0061' && c <= '\u007a')){
				enNum++;
			}
		}
		if(sentence.contains("（") && sentence.contains("）")){
			if(enNum > zhNum && zhNum >= 0 && zhNum > enNum/2){
				return "zh";
			}
		}
		if(sentence.contains("“") && sentence.contains("”")){
			if(enNum > zhNum && zhNum >= 0 && zhNum > enNum/2){
				return "zh";
			}
		}
		if(zhNum > enNum && enNum >= 0){
			return "zh";
		}
		if(enNum > zhNum && zhNum >= 0){
			return "en";
		}
		return "ot";
	}

	/**
	 * 写文件
	 * @param filePath:文件路径
	 * @param content:文件内容
	 */
	public static void writeTxtFile(String filePath, String content) {
		try {
			System.out.println("writeTxtFile   FILE_SEPARATOR ===" + FILE_SEPARATOR + "filePath == " + filePath);
			String[] tokens = filePath.split(FILE_SEPARATOR);
			String currentPath = "";
			for (int i = 0; i < tokens.length - 1; i++) {
				currentPath += tokens[i] + FILE_SEPARATOR;
				if (!isFileExist(currentPath)) {

					new File(currentPath).mkdir();
				}
			}
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8"));
			writer.write(content);
			writer.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static boolean isFileExist(String filePath) {
		try {
			File file = new File(filePath);
			return file.exists();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static boolean isWindowsPlatform() {
		String os = System.getProperty("os.name");
		if (os.toLowerCase().startsWith("win")) {
			return true;
		}
		return false;
	}

	//判断是否是签字页开始标志
	public static boolean isSignaturePageSign(String str){
		if(str == null || str.length() == 0){
			return false;
		}

		if(str.toLowerCase().equals("[signature page to follow]")
				|| str.toLowerCase().equals("[signature pages to follow]")
				|| str.toLowerCase().equals("[signature page]")
				|| str.toLowerCase().equals("[Remainder of page intentionally left blank]")
				|| str.toLowerCase().equals("[signature page follows]")
				|| str.toLowerCase().equals("[signatures begin on next page]")
				|| str.equals("[下一页为签字页]")
				|| str.equals("（以下无正文）")
				|| str.equals("[下一页开始为签字页]")
				|| str.equals("[本页以下无正文]")
				|| str.equals("[以下无正文为签字页]")
				|| str.equals("[下接签字页]")
				|| str.equals("[后接签署页]")){
			return true;
		}
		return false;
	}

	public static void parserDocxAndWrite(String docxPath,String txtPath,boolean isContents) throws Exception {

		WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(docxPath));

		MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();

		org.docx4j.wml.Document wmlDocumentEl = documentPart.getJaxbElement();

		Body body = wmlDocumentEl.getBody();

		List<Object> bodyChildren = body.getContent();

		String content = "";
		for (Object o : bodyChildren) {
			String paragraph = "";
			if (o instanceof javax.xml.bind.JAXBElement) {
				paragraph = getJAXBElementContent(o,isContents);
				paragraph = dealWithDocxParagraph(paragraph);
			} else if (o instanceof org.docx4j.wml.P) {
				try{
					paragraph = walkList(((org.docx4j.wml.P) o).getContent(),isContents);
					paragraph = dealWithDocxParagraph(paragraph);
					if(isSignaturePageSign(paragraph)){
						//签字页不要
						break;
					}
				}catch(Exception e){
					System.err.println(e.getMessage());
					continue;
				}
			}else{
				System.out.println("parserDocxAndWrite ignore o ==== " + o.getClass());
			}
			if(paragraph.length() > 0){
				content += paragraph.trim() + "\n";
			}
		}
		writeTxtFile(txtPath, content);
	}



	public static String walkList(List children,boolean isContents) {
		String line = "";
		for (Object o : children) {
			if (o instanceof javax.xml.bind.JAXBElement) {
				line = line + getJAXBElementContent(o,isContents);
			} else if (o instanceof org.w3c.dom.Node) {
				//System.out.println(" IGNORED node " + ((org.w3c.dom.Node) o).getNodeName());
			} else if (o instanceof org.docx4j.wml.R) {
				org.docx4j.wml.R run = (org.docx4j.wml.R) o;
				String tmpStr = walkList(run.getContent(),isContents);//.getRunContent()
				line = line + tmpStr;
			} else {
				System.out.println("walkList  IGNORED o === " + o.getClass().getName());
			}
		}
		return line;
	}

	public static String dfsGetP$Hyperlink(List list){
		String res = "";
		for (Object o : list) {
			if (o instanceof javax.xml.bind.JAXBElement) {
				res = res + getP$HyperlinkJAXBElementContent(o);
			}else if (o instanceof org.docx4j.wml.R) {
				org.docx4j.wml.R run = (org.docx4j.wml.R) o;
				String tmpStr = dfsGetP$Hyperlink(run.getContent());//.getRunContent()
				res = res + tmpStr;
			} else {
				System.out.println("dfsGetP$Hyperlink  IGNORED o === " + o.getClass().getName());
			}
		}
		return res;
	}

	public static String getP$HyperlinkJAXBElementContent(Object object){
		String result = "";
		String jaxbelementName = ((JAXBElement) object).getDeclaredType().getName();
		if (jaxbelementName.equals("org.docx4j.wml.Text")) {
			org.docx4j.wml.Text text = (org.docx4j.wml.Text) ((JAXBElement) object).getValue();
			result = text.getValue();
		} else if(jaxbelementName.equals("org.docx4j.wml.R$Tab")) {
			result = " "; //换成空格
		} else {
			//System.out.println("getP$HyperlinkJAXBElementContent ignore jaxbelementName ===== " + jaxbelementName);
		}
		return result;
	}

	public static String getJAXBElementContent(Object object, boolean isContents){
		String result = "";
		String jaxbelementName = ((JAXBElement) object).getDeclaredType().getName();
		if (jaxbelementName.equals("org.docx4j.wml.Text")) {
			if(!isContents){
				org.docx4j.wml.Text text = (org.docx4j.wml.Text) ((JAXBElement) object).getValue();
				result = text.getValue();
			}
		} else if(jaxbelementName.equals("org.docx4j.wml.R$Tab")) {
			result = " "; //换成空格
		} else if(jaxbelementName.equals("org.docx4j.wml.CTSmartTagRun")){
			org.docx4j.wml.CTSmartTagRun ctSmartTagRun = (org.docx4j.wml.CTSmartTagRun) ((JAXBElement) object).getValue();
			result = walkList(ctSmartTagRun.getContent(), isContents);
		}else if(isContents && jaxbelementName.equals("org.docx4j.wml.P$Hyperlink")){
			//目录
			org.docx4j.wml.P.Hyperlink hyperlink = (org.docx4j.wml.P.Hyperlink) ((JAXBElement) object).getValue();
			result = dfsGetP$Hyperlink(hyperlink.getContent());
		} else {
			//System.out.println("getJAXBElementContent ignore jaxbelementName ===== " + jaxbelementName);
		}
		return result;
	}

}


